<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>ViewModels - Fuel Documentation</title>
	<link href="../assets/css/main.css" media="screen" rel="stylesheet" />
	<script type="text/javascript" src="../assets/js/jquery-1.4.4.min.js"></script>
	<script type="text/javascript" src="../assets/js/nav.js"></script>
	<script type="text/javascript" src="../assets/js/highlight.pack.js"></script>
	<script type="text/javascript">
		$(function() {
			show_nav('general', '../');
		});
		hljs.tabReplace = '    ';
		hljs.initHighlightingOnLoad();
	</script>
</head>
<body>

	<header>
		<h1>FUEL Documentation</h1>
	</header>

	<div id="main-nav"></div>

	<section id="content">
		<h2>ViewModels</h2>
		
		<p>ViewModels are optional, if you don't need them just use <a href="views.html">Views</a>.</p>

		<h3>What is a ViewModel?</h3>

		<p>A ViewModel is a class that contains the logic that is needed to generate your views. When the controller
		is done with your user input and is done with whatever actions it needed to take, it turns execution over to
		the ViewModel to retrieve whatever data is needed for the view. A ViewModel shouldn't do any data manipulation,
		but can contain Database calls and any other retrievel or preperation operations needed to generate the View's
		data.</p>

		<h2>Creating a ViewModel</h2>

		<p>First we'll create an empty ViewModel in <kbd>app/classes/view/index.php</kbd>:</p>

		<pre><code>class View_Index extends ViewModel {}</code></pre>

		<p>Then you create the view to which this belongs in <kbd>app/views/index.php</kbd>:</p>

		<pre><code>&lt;h1&gt;&lt;?php echo $title; ?&gt;&lt;/h1&gt;


&lt;ul&gt;
&lt;?php
	foreach ($articles as $a)
	{
		echo '&lt;li&gt;'.$a-&gt;title.'&lt;/li&gt;';
	}
?&gt;
&lt;/ul&gt;</code></pre>

		<p><strong>On view names:</strong> A ViewModel and its view are expected to share the same name. Thus a
		ViewModel <kbd>View_Index</kbd> expects the view to be in <kbd>app/views/index.php</kbd>. And underscores
		work here the same as with classes, which means that the view for <kbd>View_Some_Thing</kbd> is expected
		to be in <kbd>app/views/some/thing.php</kbd>.<br />
		This default can be overwritten by setting a non-static <kbd>$_template</kbd> property in your ViewModel with the
		View name (without .php suffix).</p>

		<p>And last we'll call the ViewModel from the controller:</p>

		<pre><code>ViewModel::factory('Index');</code></pre>

		<p>Now we have everything setup there is still no data passed to the view. That still needs to get a
		<kbd>$title</kbd> string and <kbd>$articles</kbd> array passed to it. This we do by adding a view() method
		to the ViewModel which will assign this data:</p>

		<pre><code>class View_Index extends ViewModel {

	public function view()
	{
		$this->title = 'Testing this ViewModel thing';

		$this->articles = Model_Articles::find('all');
	}
}</code></pre>

		<p>And you're done.</p>

		<h2>Security</h2>

		<p>By default ViewModels sanitize anything you set in them using Secutiry::clean(). If you want to pass 
		something unfiltered you can use the method <kbd>add_raw()</kbd>.</p>
		
		<pre><code>class View_Index extends ViewModel {

	public function view()
	{
		// add it unfiltered just like you added
		// add it filtered, outputs: &amp;lt;strong&amp;gt;not bold because filtered&amp;lt;/strong&amp;gt;
		$this->title = '&lt;strong&gt;not bold because filtered&lt;/strong&gt;';

		// add it unfiltered just like you added
		$this->set_raw('title', '&lt;strong&gt;not bold because filtered&lt;/strong&gt;');
	}
}</code></pre>

		<p>If you don't want your ViewModel to work like this add a public static property <kbd>$filter_output</kbd>
		and set it to false. Now anything will be added unfiltered, if you do want some values filtered you can use
		<kbd>set_safe()</kbd> in the same way <kbd>set_raw()</kbd> was used in the example above.</p>

		<p><strong>Note on objects:</strong> Unless the passed object is of the type View, ViewModel or Closure it is
		expected to have a __toString() method and forced to be a string when output filtering is enabled. If you want
		to pass it anyway you need to use <kbd>set_raw()</kbd>, but don't forget to filter what you use!<br />
		View &amp; ViewModels are expected to contain HTML and take care of their own filtering, which is why they're
		not sanitized. Closures cannot be sanitized, and you should take care to ensure this is done within them if
		necessary.</p>

		<h2>Passing functions to views</h2>

		<p>To pass a View specific function from your ViewModel to your View you use Closures:</p>

		<pre><code>// In the ViewModel
class View_Index extends ViewModel {

	public function view()
	{
		$this->echo_upper = function($string) { echo strtoupper($string); };
	}
}

// Which you can then use in your view:
$echo_upper('this string'); // Outputs: "THIS STRING"</code></pre>

		<h2>Advanced usage</h2>

		<h3>More methods</h3>

		<p>If there are different ways of parsing the same View you can add multiple methods to the ViewModel other
		than the default view() method. To use those you need to add the method's name as the second parameter to the
		<kbd>ViewModel::factory()</kbd> method:</p>

		<pre><code>// will call other_method() upon the the ViewModel from the above example
ViewModel::factory('Index', 'other_method');</code></pre>

		<h3>Before and after methods</h3>

		<p>If you need to have some data added for all the methods in the ViewModel you can add a <kbd>before()</kbd>
		or <kbd>after()</kbd> method just like you can with Controllers.</p>

		<h3>Changing the template</h3>
		
		<p>By default the <kbd>$this->_template</kbd> gets a View object assigned to it. You can replace this object by
		making your own <kbd>set_template()</kbd> method in the ViewModel and returning an object of your choosing.<br />
		This object must however allow you to set properties on it (which are used as the template data) and must have 
		a <kbd>__toString()</kbd> magic method that will render and return the parsed contents.<br />
		The name of the expected view is available in the <kbd>$this->_template</kbd> property.</p>

		<h3>Using ViewModels from other namespaces or not View_ prefixed</h3>

		<p>If you want to use these you have to use the full classname with the factory(), including the namespace.
		In these cases the default naming will often not work as expected so setting the <kbd>$_template</kbd>
		property is encouraged.</p>

	</section>

	<section id="footer">
		<p>
			<a href="http://fuelphp.com">Fuel PHP</a> is released under the MIT license.<br />
			&copy; 2010 - 2011 Fuel Development Team
		</p>
	</section>

</body>
</html>